package com.example.demo.order.lock;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName DistributedLockUtil
 * @Description Redis版分布式锁工具类
 * @Author MingChang.Wei
 * @Date 2025/9/26
 **/
@Slf4j
@Component("distributedLockUtil")
public class DistributedLockUtil {
    
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String LOCK_PREFIX = "distributed_lock:";
    
    // Lua脚本：释放锁时验证锁的拥有者
    private static final String UNLOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then\n" +
        "    return redis.call('del', KEYS[1])\n" +
        "else\n" +
        "    return 0\n" +
        "end";
    
    /**
     * 尝试获取分布式锁
     * @param lockKey 锁的key
     * @param lockValue 锁的值（用于验证锁的拥有者，通常使用UUID）
     * @param expireSeconds 锁的过期时间（秒）
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String lockValue, long expireSeconds) {
        try {
            if (StrUtil.isBlank(lockKey) || StrUtil.isBlank(lockValue)) {
                log.warn("锁key或锁值不能为空");
                return false;
            }
            
            String key = LOCK_PREFIX + lockKey;
            
            // 使用 SET key value NX EX seconds 命令
            Boolean success = redisTemplate.opsForValue().setIfAbsent(key, lockValue, expireSeconds, TimeUnit.SECONDS);
            
            if (Boolean.TRUE.equals(success)) {
                log.debug("获取分布式锁成功: key={}, value={}, expire={}s", lockKey, lockValue, expireSeconds);
                return true;
            } else {
                log.debug("获取分布式锁失败: key={}, value={}", lockKey, lockValue);
                return false;
            }
        } catch (Exception e) {
            log.error("获取分布式锁异常: lockKey={}", lockKey, e);
            return false;
        }
    }
    
    /**
     * 释放分布式锁
     * @param lockKey 锁的key
     * @param lockValue 锁的值（用于验证锁的拥有者）
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String lockValue) {
        try {
            if (StrUtil.isBlank(lockKey) || StrUtil.isBlank(lockValue)) {
                log.warn("锁key或锁值不能为空");
                return false;
            }
            
            String key = LOCK_PREFIX + lockKey;
            
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptText(UNLOCK_SCRIPT);
            script.setResultType(Long.class);
            
            Long result = redisTemplate.execute(script, Collections.singletonList(key), lockValue);
            
            if (result != null && result > 0) {
                log.debug("释放分布式锁成功: key={}, value={}", lockKey, lockValue);
                return true;
            } else {
                log.warn("释放分布式锁失败，锁不存在或不属于当前持有者: key={}, value={}", lockKey, lockValue);
                return false;
            }
        } catch (Exception e) {
            log.error("释放分布式锁异常: lockKey={}", lockKey, e);
            return false;
        }
    }
    
    /**
     * 尝试获取锁，支持重试
     * @param lockKey 锁的key
     * @param lockValue 锁的值
     * @param expireSeconds 锁的过期时间（秒）
     * @param retryTimes 重试次数
     * @param retryIntervalMs 重试间隔（毫秒）
     * @return 是否获取成功
     */
    public boolean tryLockWithRetry(String lockKey, String lockValue, long expireSeconds, int retryTimes, long retryIntervalMs) {
        for (int i = 0; i <= retryTimes; i++) {
            if (tryLock(lockKey, lockValue, expireSeconds)) {
                return true;
            }
            
            if (i < retryTimes) {
                try {
                    Thread.sleep(retryIntervalMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("获取锁重试被中断: lockKey={}", lockKey);
                    return false;
                }
            }
        }
        
        log.warn("获取分布式锁失败，已重试{}次: lockKey={}", retryTimes, lockKey);
        return false;
    }
    
    /**
     * 检查锁是否存在
     */
    public boolean isLocked(String lockKey) {
        try {
            String key = LOCK_PREFIX + lockKey;
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("检查锁状态异常: lockKey={}", lockKey, e);
            return false;
        }
    }
    
    // ========== 业务相关的锁key生成方法 ==========
    
    /**
     * 购物车操作锁
     */
    public String getCartLockKey(Long userId) {
        return "cart:user:" + userId;
    }
    
    /**
     * 订单创建锁
     */
    public String getOrderCreateLockKey(Long userId) {
        return "order:create:user:" + userId;
    }
    
    /**
     * 支付锁
     */
    public String getPaymentLockKey(String orderNo) {
        return "payment:order:" + orderNo;
    }
    
    /**
     * 库存锁
     */
    public String getStockLockKey(Long productId) {
        return "stock:product:" + productId;
    }
    
    /**
     * 用户操作锁（防止并发操作）
     */
    public String getUserOperationLockKey(Long userId, String operation) {
        return "user:operation:" + userId + ":" + operation;
    }
}
